﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using CaiyinSoft.NetGather.Protocol;
using Microsoft.CSharp;

namespace CaiyinSoft.NetGather.NetGatherLib.Filter.Process
{
    [Serializable]
    public class CSharpCodeHTMLFilter : HTMLFilterBase
    {
        public override string FilterCaption
        {
            get
            {
                return "自定义过滤器";
            }
        }

        public override string ExpressionCaption
        {
            get
            {
                return "C#代码";
            }
        }

        public override string Description
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("输入C#代码,处理字符串.方法签名:");
                sb.AppendLine("public string MethodInstance(string inParam)");
                sb.AppendLine("示例:");
                sb.AppendLine("System.Text.StringBuilder sb = new System.Text.StringBuilder();");
                sb.AppendLine("for (int i = 0; i < 99; i++)");
                sb.AppendLine("{");
                sb.AppendLine("    sb.AppendLine(inParam);");
                sb.AppendLine("}");
                sb.AppendLine("return sb.ToString();");
                return sb.ToString();
            }
        }

        //public int ProcessType { get; private set; }

        public CSharpCodeHTMLFilter()
            : base()
        {
            _InitParams();
        }

        private void _InitParams()
        {
            SetInputHTML("");
            OutputKey = Guid.NewGuid().ToString();
        }

        public override List<FilterResult> FilterHTML()
        {
            int parentHTMLPosition = 0;
            if (ParentFilter != null)
                parentHTMLPosition = ParentFilter.HTMLPosition;
            HTMLPosition = parentHTMLPosition;

            List<FilterResult> parentResults = GetInputHTML();
            if (parentResults == null || parentResults.Count == 0) return null;
            List<FilterResult> listRevalue = new List<FilterResult>();

            CaiyinSoft.NetGather.NetGatherLib.Filter.Process.Context cxt = new CaiyinSoft.NetGather.NetGatherLib.Filter.Process.Context();
            string tmp = "";
            foreach (var pr in parentResults)
            {
                tmp = cxt.Invoke(pr.HTMLResult, this.FilterExpression);
                FilterResult result = new FilterResult(pr.HTMLPosition, tmp);
                listRevalue.Add(result);

            }
            return listRevalue; // 

        }
    }


    #region Compiler

    public class Context
    {
        public CSharpCodeProvider CodeProvider { get; set; }
        public IDictionary<String, Assembly> Assemblys { get; set; }

        public Context()
        {
            CodeProvider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
            Assemblys = new Dictionary<String, Assembly>();
            Assembly[] al = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly a in al)
            {
                AddAssembly(a);
            }
            AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
        }

        private void AddAssembly(Assembly a)
        {
            if (a != null)
            {
                Assemblys.Add(a.FullName, a);
            }
        }

        void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
        {
            Assembly a = args.LoadedAssembly;
            if (!Assemblys.ContainsKey(a.FullName))
            {
                AddAssembly(a);
            }
        }

        public CompilerParameters CreateCompilerParameters()
        {
            CompilerParameters cp = new CompilerParameters();
            cp.GenerateExecutable = false;
            cp.GenerateInMemory = true;
            if (Assemblys != null)
            {
                foreach (Assembly a in Assemblys.Values)
                {
                    cp.ReferencedAssemblies.Add(a.Location);
                }
            }
            return cp;
        }

        public string Invoke(string inParam, String sourceCode)
        {
            String inputCmdString = sourceCode.Trim();
            if (String.IsNullOrEmpty(inputCmdString)) return "";

            String fullCmd = BuildFullCmd(inputCmdString);

            CompilerResults cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(), fullCmd);

            if (cr.Errors.HasErrors)
            {
                Boolean recompileSwitch = true;

                foreach (CompilerError err in cr.Errors)
                {
                    //CS0201 : Only assignment, call, increment, decrement, and new object expressions can be
                    //used as a statement
                    if (!err.ErrorNumber.Equals("CS0201"))
                    {
                        recompileSwitch = false;
                        break;
                    }
                }

                //// 重新编译
                if (recompileSwitch)
                {
                    //String dynaName = "TempArg_Dynamic_" + DateTime.Now.Ticks.ToString();
                    //inputCmdString = String.Format("  var {0} = ", dynaName) + inputCmdString;
                    //inputCmdString += ";\n  System.Console.WriteLine(" + dynaName + ");";

                    //fullCmd = BuildFullCmd(inputCmdString);
                    cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(), fullCmd);
                }

                if (cr.Errors.HasErrors)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendLine("编译错误：");
                    foreach (CompilerError err in cr.Errors)
                    {
                        sb.AppendLine(err.ErrorNumber);
                        sb.AppendLine(err.ErrorText);
                    }
                    return sb.ToString();
                }
            }

            Assembly assem = cr.CompiledAssembly;
            Object dynamicObject = assem.CreateInstance("CaiYinSoft.DynamicClass");
            Type t = assem.GetType("CaiYinSoft.DynamicClass");
            MethodInfo minfo = t.GetMethod("MethodInstance");
            return (string)minfo.Invoke(dynamicObject, new object[] { inParam });
        }

        private String BuildFullCmd(String inputCmdString)
        {
            String fullCmd = String.Empty;

            fullCmd += @"
                namespace CaiYinSoft 
                { 
                    public class DynamicClass
                    {
                        public string MethodInstance(string inParam)
                        {
                            " + inputCmdString + @"
                        }
                    }
                }";
            return fullCmd;
        }
    }

    #endregion
}
